package com.lahuan.config.datasouce;

import com.lahuan.config.datasouce.entity.GlobalXid;
import com.lahuan.config.datasouce.entity.XaBranch;
import com.mysql.cj.jdbc.MysqlXid;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import java.util.HashSet;
import java.util.Random;
import java.util.UUID;

/**
 * @author lfy
 */
@Aspect
@Component
public class XaAopTransactionManager {


    /**
     * 环绕增强
     * @param proceedingJoinPoint
     * @param xa
     * @return
     * @throws Throwable
     */
    @Around("@annotation(xa)")
    public Object around(ProceedingJoinPoint proceedingJoinPoint, XaTransaction xa) throws Throwable {
        try {
            System.out.println("进入aop");
            GlobalXid globalXid = genXid();
            XaTransactionHolder.setGlobalXid(globalXid);
            Object result = proceedingJoinPoint.proceed();
            twoPhaseCommit();
            return result;
        } catch (Throwable throwable) {
            //出现异常直接回滚
            transactionRollback();
            throw throwable;
        }finally {
            try {
                XaTransactionHolder.clear();
            }catch (Exception ex) {
                System.out.println("清空xa事务上下文出现异常");
                ex.printStackTrace();
            }
        }

    }

    private GlobalXid genXid() {
        //xid生成，这里用随机数来简化，实际环境需要保证唯一
        Random rd = new Random();
        int format = rd.nextInt(Integer.MAX_VALUE);
        byte[] gtrid = UUID.randomUUID().toString().getBytes();
        GlobalXid globalXid = new GlobalXid();
        globalXid.setFormatId(format);
        globalXid.setGtrid(gtrid);
        return globalXid;
    }


    /**
     * 两阶段提交,
     * @throws XAException
     */
    private void twoPhaseCommit() throws XAException {
        HashSet<XaBranch> set = XaTransactionHolder.getXaBranchSet();
        if(set ==null || set.size()==0){
            return ;
        }
        //阶段1 prepare全部各个事务
        System.out.println("prepare阶段");
        for (XaBranch branch : set) {
            MysqlXid mysqlXid = branch.getMysqlXid();
            XAResource rm = branch.getRm();
            if(rm.prepare(mysqlXid)!=XAResource.XA_OK){
                transactionRollback();
                return;
            }

        }
        //阶段2 全部事务提交
        transactionCommit();
    }

    /**
     * 把所有的xa连接事务都提交
     * @throws XAException
     */
    private void transactionCommit() throws XAException {
        HashSet<XaBranch> set = XaTransactionHolder.getXaBranchSet();
        if(set ==null || set.size()==0){
            return ;
        }
        System.out.println("全部commit");
        for (XaBranch branch : set) {
            MysqlXid mysqlXid = branch.getMysqlXid();
            XAResource rm = branch.getRm();
            rm.commit(mysqlXid, false);
        }
    }
    /**
     * 把所有的xa连接事务都回滚
     * @throws XAException
     */
    private void transactionRollback() throws XAException {
        HashSet<XaBranch> set = XaTransactionHolder.getXaBranchSet();
        if(set ==null || set.size()==0){
            return ;
        }
        System.out.println("全部rollback");
        for (XaBranch branch : set) {
            MysqlXid mysqlXid = branch.getMysqlXid();
            XAResource rm = branch.getRm();
            rm.rollback(mysqlXid);
        }
    }

}
